#include "scheduler.h"
#include "fiber.h"
#include "hook.h"
#include <iostream>
namespace fiber 
{
// 线程局部变量：当前线程的调度器实例（线程级调度器）
static thread_local Scheduler* cur_scheduler = nullptr;
// 线程局部变量：当前线程的调度协程（每个线程一个，协程级调度器）
static thread_local Fiber* cur_scheduler_fiber = nullptr;

// 日志前缀
const std::string LOG_HEAD = "[scheduler] ";

Scheduler::Scheduler(size_t threads, bool use_caller, const std::string &name)
: m_name(name), m_isUseCaller(use_caller) {
    CondPanic(threads > 0, "threads <= 0");  // 确保线程数大于0

    // 这里项目实现的是让主线程创建调度器，而不是额外创建一个调度线程
    if (use_caller) {
        std::cout << LOG_HEAD << "current thread as called thread" << std::endl;
        --threads;  // 总线程数减1（因为caller线程也算作工作线程）
    
        // 初始化caller线程的主协程（获取当前协程）
        // 由于当前为一个新的线程，里面没有协程，因此get得到的是新创建的主协程
        Fiber::GetThis();
        std::cout << LOG_HEAD << "init caller thread's main fiber success" << std::endl;
    
        // 确保当前调度器未被初始化
        CondPanic(GetThis() == nullptr, "GetThis err:cur scheduler is not nullptr");
        setThis();  // 设置当前线程的调度器为自身
    
        /*
         * 初始化caller线程的调度协程（schedulerFiber）
         * 该协程执行调度逻辑（run方法），调度结束后返回主协程
         * 要注意这里的调度协程和调度器的关系
         * 所有线程共用一个调度器
         * 但是调度协程是每个线程里都有一个，并且在从属线程里调度携程就是主协程
         */
        m_schedulerFiber.reset(new Fiber(std::bind(&Scheduler::run, this), 0, false));
        std::cout << LOG_HEAD << "init caller thread's caller fiber success" << std::endl;
    
        Thread::SetName(m_name);  // 设置线程名称
        cur_scheduler_fiber = m_schedulerFiber.get();  // 记录当前线程的调度协程
        m_rootThread = GetThreadId();  // 记录caller线程ID
        m_threadIds.push_back(m_rootThread);  // 加入线程ID列表
    } 
    else {
        m_rootThread = -1;  // 不使用caller线程时，rootThread设为-1
    }
    m_threadCnt = threads;  // 记录工作线程数量
    std::cout << "-------scheduler init success-------" << std::endl;
}

Scheduler::~Scheduler() {
    CondPanic(m_isStopped, "isstopped is false");  // 确保调度器已停止
    if (GetThis() == this) {
        cur_scheduler = nullptr;  // 清除当前线程的调度器引用
    }
}

// 获取当前线程的调度器实例
Scheduler* Scheduler::GetThis() { return cur_scheduler; }

// 获取当前线程的调度协程
Fiber* Scheduler::GetSchedulerFiber() { return cur_scheduler_fiber; }

// 设置当前线程的调度器为自身
void Scheduler::setThis() { cur_scheduler = this; }

void Scheduler::start() {
    std::cout << LOG_HEAD << "scheduler start" << std::endl;
    Mutex::Lock lock(m_mutex);  // 加锁保护线程池初始化
    if (m_isStopped) {
        std::cout << "scheduler has stopped" << std::endl;
        return;
    }
    CondPanic(m_threadPool.empty(), "thread pool is not empty");  // 确保线程池为空
  
    // 创建工作线程
    m_threadPool.resize(m_threadCnt);
    for (size_t i = 0; i < m_threadCnt; i++) {
        // 每个线程执行调度逻辑（run方法）
        m_threadPool[i].reset(new Thread(std::bind(&Scheduler::run, this), m_name + "_" + std::to_string(i)));
        m_threadIds.push_back(m_threadPool[i]->getId());  // 记录线程ID
    }
}

void Scheduler::stop() {
    std::cout << LOG_HEAD << "stop" << std::endl;
    if (stopping()) {
        return;
    }

    m_isStopped = true;  // 标记为停止状态
  
    // 校验调用线程（仅caller线程可调用）
    if (m_isUseCaller) {
        CondPanic(GetThis() == this, "cur thread is not caller thread");
    } 
    else {
        CondPanic(GetThis() != this, "cur thread is caller thread");
    }
  
    // 通知所有工作线程停止
    for (size_t i = 0; i < m_threadCnt; i++) {
        tickle();
    }
    // 通知caller线程的调度协程
    if (m_schedulerFiber) {
        tickle();
    }
  
    // 等待caller线程的调度协程执行完毕
    if (m_schedulerFiber) {
        m_schedulerFiber->resume();  // 切换到调度协程，执行剩余任务
        std::cout << "scheduler fiber end" << std::endl;
    }
  
    // 回收工作线程资源
    std::vector<Thread::ptr> threads;
    {
        Mutex::Lock lock(m_mutex);
        threads.swap(m_threadPool);  // 转移线程所有权
    }
    for (auto &i : threads) {
        i->join();  // 等待线程结束
    }
}

// 通知其他线程有任务待处理（此处简化实现，实际可能用条件变量等）
void Scheduler::tickle() { std::cout << "tickle" << std::endl; }

void Scheduler::run() {
    std::cout << LOG_HEAD << "begin run" << std::endl;
    set_hook_enable(true);  // 启用钩子（可能用于系统调用hook，实现协程切换）
    setThis();  // 设置当前线程的调度器为自身
  
    // 非caller线程需要初始化自身的调度协程
    if (GetThreadId() != m_rootThread) {
        // 这里没有协程，因此GetThis得到的是线程的主协程
        // 但是这里让主协程充当调度协程
        cur_scheduler_fiber = Fiber::GetThis().get();
    }
  
    // 创建idle协程（无任务时执行）
    Fiber::ptr idleFiber = std::make_shared<Fiber>(std::bind(&Scheduler::idle, this));
    Fiber::ptr cbFiber;  // 用于执行函数任务的临时协程
  
    SchedulerTask task;  // 任务对象（包含协程或函数）
    while (true) {
        task.reset();  // 重置任务
        bool tickle_me = false;  // 是否需要通知其他线程取任务
  
        {
            // 这里是要访问调度器的任务队列，多个线程会同时访问，因此需要使用线程级别的锁
            Mutex::Lock lock(m_mutex);  // 加锁访问任务队列
            auto it = m_tasks.begin();
            while (it != m_tasks.end()) {
                // 任务指定了调度线程，但不是当前线程，需要通知对应线程
                if (it->m_thread != -1 && it->m_thread != GetThreadId()) {
                    ++it;
                    tickle_me = true;  // 需要通知其他线程
                    continue;
                }
    
                // 任务必须包含协程或函数
                CondPanic(it->m_fiber || it->m_cb, "task is nullptr");
                // 协程任务必须处于就绪状态
                if (it->m_fiber) {
                    CondPanic(it->m_fiber->getState() == Fiber::State::READY, "fiber task state error");
                }
    
                // 取出任务，准备执行
                task = *it;
                m_tasks.erase(it++);  // 从队列移除
                ++m_activeThreadCnt;  // 活跃线程数+1
                break;
            }
            // 若队列中还有任务，需要通知其他线程
            tickle_me |= (it != m_tasks.end());
        }
  
        // 通知其他线程取任务
        if (tickle_me) {
            tickle();
        }
  
        // 执行协程任务
        if (task.m_fiber) {
            task.m_fiber->resume();  // 恢复协程执行
            --m_activeThreadCnt;     // 活跃线程数-1
            task.reset();            // 重置任务
        }
        // 执行函数任务（包装为协程）
        else if (task.m_cb) {
            if (cbFiber) {
                // 这里使用的是协程的重置
                cbFiber->reset(task.m_cb);  // 复用现有协程
            } 
            else {
                // 这里使用的是智能指针的重置
                cbFiber.reset(new Fiber(task.m_cb));  // 创建新协程
            }

            task.reset();
            cbFiber->resume();  // 执行函数
            --m_activeThreadCnt;
            cbFiber.reset();
        }
        // 无任务时执行idle协程
        else {
            if (idleFiber->getState() == Fiber::State::TERM) {
                std::cout << "idle fiber term" << std::endl;
                break;  // idle协程结束，退出调度循环
            }
            ++m_idleThreadCnt;       // 空闲线程数+1
            idleFiber->resume();     // 执行idle协程（让出CPU）
            --m_idleThreadCnt;       // 空闲线程数-1
        }
    }

    std::cout << "run exit" << std::endl;
}

void Scheduler::idle() {
    while (!stopping()) {
        Fiber::GetThis()->yield();  // 协程让出CPU
        // 这里调度协程（主协程）和空闲协程互相运行
    }
}

bool Scheduler::stopping() {
    Mutex::Lock lock(m_mutex);
    return m_isStopped && m_tasks.empty() && m_activeThreadCnt == 0;
}

}  // namespace fiber